<?php
Cinnamon::loadClass('Event');


/**
 * Default event dispatcher
 */
class EventDispatcher {
	/**
	 * Registered events and their listeners
	 */
	protected $events;
	
	/**
	 * Default constructor
	 */
	public function __construct() {
		$this->events = array();
	}
	
	/**
	 * Registers a class listener. Each method of this instance that matches "on[event]" will be registered for the
	 corresponding event. For an example the method "onInitialize" will be registered for the event "Initialize",
	 while the method "onTrack" will be registered for the event "Track"\
	 * @param object listener the listener to be registered
	 * @param boolean true only and only if the listener has been added successfully
	 */
	public function addListener($listener) {
		if ( !is_object($listener) ) {
			return false;
		}
		
		$methods = get_class_methods($listener);
		foreach ( $methods as $method ) {
			if ( substr($method, 0, 2) == 'on' ) {
				$event = substr($method, 2); 
				$this->addEventListener($event, array(& $listener, $method));
			}			
		}
		return true;
	}
	
	/** 
	 * Registers a callable function as a listener for the specified event. The callable function may be
	 a function or a class method
	 *
	 * @param string event the event name
	 * @param mixed listener the function or method name
	 */
	public function addEventListener($event, $listener) {
		$event = strtolower($event);
		$this->events[$event][] = $listener;
	}

	/**
	 * Unregisters a class listener. Each method of this instance that matches "on[event]" will be registered for the
	 corresponding event. See "addListener" method for more details on this.
	 *
	 * @param object listener the listener to be unregistered
	 */
	public function removeListener($listener) {
		if ( !is_object($listener) ) {
			return false;
		}
		
		$methods = get_class_methods($listener);
		foreach ( $methods as $method ) {
			if ( substr($method, 0, 2) == 'on' ) {
				$event = substr($method, 2);
				$this->removeEventListener($event, array(& $listener, $method));
			}			
		}
		return true;	
	}
	
	/**
	 * Unregisters a callable function for the specified event. The callable function may be
	 a function or a class method
	 *
	 * @param string event the event name
	 * @param mixed listener the function or method name
	 */
	public function removeEventListener($event, $listener) {
		$event = strtolower($event);
		$this->events[$event] = array_remove($this->events[$event], $listener);
	}	
	
	/**
	 * Dispatches the specified event and all the related registered listeners will be notified
	 * 
	 * @param object event an instance of Event object
	 */
	public function dispatchEvent($event) {
		$eventType = strtolower($event->type);
		foreach ( (array) $this->events[$eventType] as $listener ) {
			if ( is_callable($listener) ) {
				call_user_func($listener, $event);
			}
		}	
	}
}
?>